package hulppakket;

import geometry.BVHNode;
import geometry.Primitive;
import geometry.Shape;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import lights.Light;
import materials.Material;
import textures.Texture;
import transformation.Matrix4f;
import transformation.TransformationStack;

public class Scene {

	private static HashMap<String, Camera> cameras = new HashMap<String, Camera>();
	private static HashMap<String, Light> lights = new HashMap<String, Light>();
	private static HashMap<String, Material> materials = new HashMap<String, Material>();
	private static HashMap<String, Texture> textures = new HashMap<String, Texture>();
	public static void addCamera(Camera camera) {
		Scene.cameras.put(camera.getName(), camera);
	}
	public static void addLight(Light light) {
		Scene.lights.put(light.getName(), light);
	}
	public static void addMaterial(Material material) {
		Scene.materials.put(material.getName(), material);
	}
	private Color3f backgroundColor;
	private Camera currentCamera;
	private ArrayList<Light> currentLights;

	private TransformationStack matrixStack;

	private BVHNode root;

	private ArrayList<Shape> shapes;

	public Scene() {
		initializeDatastructures();
	}

	public void addShape(Shape shape) {
		this.shapes.add(shape);
	}

	public Color3f getBackgroundColor() {
		return this.backgroundColor;
	}

	public Camera getCurrentCamera() {
		return this.currentCamera;
	}

	public ArrayList<Light> getCurrentLights() {
		return this.currentLights;
	}

	public Intersection getIntersection(float ia, float ja) {
		float far = this.currentCamera.getFar();
		float near = this.currentCamera.getNear();
		Intersection intersection = null;
		Ray ray = this.getCurrentCamera().getRay(ia, ja);
		BVHNode root = this.getRoot();
		intersection = root.intersect(ray, near, far);
		return intersection;
	}

	public Matrix4f getInverseStackMatrix() {
		return this.matrixStack.getInverseMatrix();
	}

	public Material getMaterial(String name) {
		return Scene.materials.get(name);
	}

	public ArrayList<Primitive> getPrimitives() {
		ArrayList<Primitive> result = new ArrayList<Primitive>();
		for (Shape shape : shapes) {
			result.addAll(shape.getTriangles());
		}
		return result;
	}

	private BVHNode getRoot() {
		if (root == null) {
			System.out.println("Start building tree...");
			this.root = new BVHNode(this.getPrimitives(), 0);
			System.out.println("Tree built!");
		}
		return this.root;
	}

	public Matrix4f getStackMatrix() {
		return this.matrixStack.getMatrix();
	}

	public Texture getTexture(String name) {
		return Scene.textures.get(name);
	}

	private void initializeDatastructures() {
		currentLights = new ArrayList<Light>();
		shapes = new ArrayList<Shape>();
		matrixStack = new TransformationStack();
		root = null;
	}

	public boolean isBlocked(Intersection intersection, Vector3f lightVector) {
		float near = 0.001f;
		float far = Float.MAX_VALUE;
		Ray ray = new Ray(intersection.getHitpoint(), lightVector);
		return (this.root.intersect(ray, near, far) != null);
	}

	public void popMatrix() {
		this.matrixStack.pop();
	}

	public void pushMatrix(Matrix4f matrix, Matrix4f inverseMatrix) {
		this.matrixStack.push(matrix, inverseMatrix);
	}

	public void setBackgroundColor(Color3f color) {
		this.backgroundColor = color;
	}

	public void setCurrentCamera(String name) {
		this.currentCamera = cameras.get(name);
	}

	public void setCurrentLights(String[] lightNames) {
		for (int i = 0; i < lightNames.length; i++) {
			this.currentLights.add(lights.get(lightNames[i]));
		}
	}
	public static void addTexture(String src, String name) throws IOException {
		Scene.textures.put(name, new Texture(src, name));
	}
}
